/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.hasor.search.query;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
 * This class is similar to MultiMapSolrParams except you can edit the 
 * parameters after it is initialized.  It has helper functions to set/add
 * integer and boolean param values.
 * 
 * @since solr 1.3
 */
public class ModifiableSearchParams extends SearchParams {
    private static final long     serialVersionUID = 7835722691489174048L;
    private Map<String, String[]> vals;
    public ModifiableSearchParams() {
        // LinkedHashMap so params show up in CGI in the same order as they are entered
        vals = new LinkedHashMap<String, String[]>();
    }
    /** Constructs a new ModifiableSolrParams directly using the provided Map&lt;String,String[]&gt; */
    public ModifiableSearchParams(Map<String, String[]> v) {
        vals = v;
    }
    /** Constructs a new ModifiableSolrParams, copying values from an existing SolrParams */
    public ModifiableSearchParams(SearchParams params) {
        vals = new LinkedHashMap<String, String[]>();
        if (params != null) {
            this.add(params);
        }
    }
    //----------------------------------------------------------------
    /** Replace any existing parameter with the given name.  if val==null remove key from params completely. */
    public ModifiableSearchParams set(String name, String... val) {
        if (val == null || (val.length == 1 && val[0] == null)) {
            vals.remove(name);
        } else {
            vals.put(name, val);
        }
        return this;
    }
    public ModifiableSearchParams set(String name, int val) {
        set(name, String.valueOf(val));
        return this;
    }
    public ModifiableSearchParams set(String name, boolean val) {
        set(name, String.valueOf(val));
        return this;
    }
    /**
     * Add the given values to any existing name
     * @param name Key
     * @param val Array of value(s) added to the name. NOTE: If val is null
     *      or a member of val is null, then a corresponding null reference will be included
     *      when a get method is called on the key later.
     *  @return this
     */
    public ModifiableSearchParams add(String name, String... val) {
        String[] old = vals.put(name, val);
        if (old != null) {
            if (val == null || val.length < 1) {
                String[] both = new String[old.length + 1];
                System.arraycopy(old, 0, both, 0, old.length);
                both[old.length] = null;
                vals.put(name, both);
            } else {
                String[] both = new String[old.length + val.length];
                System.arraycopy(old, 0, both, 0, old.length);
                System.arraycopy(val, 0, both, old.length, val.length);
                vals.put(name, both);
            }
        }
        return this;
    }
    public void add(SearchParams params) {
        Iterator<String> names = params.getParameterNamesIterator();
        while (names.hasNext()) {
            String name = names.next();
            set(name, params.getParams(name));
        }
    }
    /** remove a field at the given name */
    public String[] remove(String name) {
        return vals.remove(name);
    }
    /** clear all parameters */
    public void clear() {
        vals.clear();
    }
    /** 
     * remove the given value for the given name
     * @return true if the item was removed, false if null or not present
     */
    public boolean remove(String name, String value) {
        String[] tmp = vals.get(name);
        if (tmp == null)
            return false;
        for (int i = 0; i < tmp.length; i++) {
            if (tmp[i].equals(value)) {
                String[] tmp2 = new String[tmp.length - 1];
                if (tmp2.length == 0) {
                    tmp2 = null;
                    remove(name);
                } else {
                    System.arraycopy(tmp, 0, tmp2, 0, i);
                    System.arraycopy(tmp, i + 1, tmp2, i, tmp.length - i - 1);
                    set(name, tmp2);
                }
                return true;
            }
        }
        return false;
    }
    //----------------------------------------------------------------
    public Map<String, String[]> toMap() {
        return this.vals;
    }
    @Override
    public String get(String param) {
        String[] v = vals.get(param);
        if (v != null && v.length > 0) {
            return v[0];
        }
        return null;
    }
    @Override
    public Iterator<String> getParameterNamesIterator() {
        return vals.keySet().iterator();
    }
    public Set<String> getParameterNames() {
        return vals.keySet();
    }
    @Override
    public String[] getParams(String param) {
        return vals.get(param);
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(128);
        try {
            boolean first = true;
            for (Map.Entry<String, String[]> entry : vals.entrySet()) {
                String key = entry.getKey();
                String[] valarr = entry.getValue();
                for (String val : valarr) {
                    if (!first)
                        sb.append('&');
                    first = false;
                    sb.append(key);
                    sb.append('=');
                    if (val != null) {
                        sb.append(URLEncoder.encode(val, "UTF-8"));
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } // can't happen
        return sb.toString();
    }
}